﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Collections.ObjectModel;

namespace SDF
{
    public static class SmartLogicalTreeHelper
    {
        #region Attached Properties

        private static readonly DependencyPropertyKey ParentPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
            "Parent",
            typeof(DependencyObject),
            typeof(SmartLogicalTreeHelper),
            new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty ParentProperty = ParentPropertyKey.DependencyProperty;

        private static readonly DependencyPropertyKey ChildrenPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
            "Children",
            typeof(ObservableCollection<DependencyObject>),
            typeof(SmartLogicalTreeHelper),
            new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty ChildrenProperty = ChildrenPropertyKey.DependencyProperty;

        #endregion

        #region Methods

        public static DependencyObject GetParent(DependencyObject child)
        {
            if (child == null)
                return null;

            return (DependencyObject)child.GetValue(ParentProperty);
        }

        private static void SetParent(DependencyObject child, DependencyObject parent)
        {
            if (child == null)
                return;

            IValidateParent child2 = child as IValidateParent;
            if ((child2 != null) && (parent != null) && !child2.IsValidParent(parent))
                throw new InvalidOperationException(String.Format("{0} cannot be added as a child of {1}.", child.GetType(), parent.GetType()));

            child.SetValue(ParentPropertyKey, parent);
        }

        public static ObservableCollection<DependencyObject> GetChilden(DependencyObject parent)
        {
            if (parent == null)
                return null;

            var children = (ObservableCollection<DependencyObject>)parent.GetValue(ChildrenProperty);
            if (children == null)
            {
                children = new ObservableCollection<DependencyObject>();
                parent.SetValue(ChildrenPropertyKey, children);
            }
            return children;
        }

        public static void AddChild(DependencyObject parent, DependencyObject child)
        {
            if (parent == null)
                throw new ArgumentNullException("parent");

            if (child == null)
                throw new ArgumentNullException("child");

            var currentParent = GetParent(child);
            if (currentParent != null)
                throw new InvalidOperationException(String.Format("You must first disconnect the {0} from its current parent.", child.GetType()));

            SetParent(child, parent);
            var children = GetChilden(parent);
            if (!children.Contains(child))
                children.Add(child);
        }

        public static void RemoveChild(DependencyObject parent, DependencyObject child)
        {
            if (parent == null)
                throw new ArgumentNullException("parent");

            if (child == null)
                throw new ArgumentNullException("child");

            var currentParent = GetParent(child);
            if (currentParent != parent)
                throw new InvalidOperationException(String.Format("The {0} is not a child of the given object.", child.GetType()));

            SetParent(child, null);
            var children = GetChilden(parent);
            if (children.Contains(child))
                children.Remove(child);
        }

        #endregion
    }
}
